require('cloud/app.js');
require('cloud/nicetips.js');
var _ = require('underscore.js');


// We include google places module
var googlePlaces = require('cloud/google_places.js');
googlePlaces.initialize('AIzaSyAjFHT51yR30a9iDjwFc0sy0N3QHefssBo');


// Use Parse.Cloud.define to define as many cloud functions as you want.
// For example:
Parse.Cloud.define("hello", function(request, response) {
    response.success("Hello world! " + googlePlaces.testMethod(3,4));
});


// This is a function that accesses a GooglePlace service to get the response from it
Parse.Cloud.define("freeSearch", function(request, response) {
    // We read parameter from the request
    var freeText = request.params.freeText;
    var countryCode = request.params.countryCode;
    // Define callback function for success
    var successFunction =  function(httpResponse) {
        // We parse the text as a JSON object
        var jsonResult = JSON.parse(httpResponse.text);
        // For each item in results we get the id and search for the same in local places
        var results = jsonResult.results;
        for (var i = 0; i < results.length; i++){
            // We get the current place. We search for the place if present into the 
            // local DB
            console.log("CHECKING PLACE WITH ID :" + results[i].id);
        }
        // We have to decorate the JSON with local information (ref or people)
        response.success(jsonResult);
    }
    // Define callback function for error
    var errorFunction =  function(httpResponse) {
        response.success(httpResponse.text);
    }
    // We read the input parameters
    googlePlaces.freeSearch(freeText, countryCode,successFunction, errorFunction);
});

// This is a function that accesses a GooglePlace service to get the response from it
Parse.Cloud.define("similarPlacesSearch", function(request, response) {
    // We read parameter from the request
    var freeText = request.params.freeText;
    var countryCode = request.params.countryCode;
    // Define callback function for success
    var successFunction =  function(httpResponse) {
        // We parse the text as a JSON object
        var jsonResult = JSON.parse(httpResponse.text);
        // We have to decorate the JSON with local information (ref or people)
        response.success(jsonResult);
    }
    // Define callback function for error
    var errorFunction =  function(httpResponse) {
        response.success(httpResponse.text);
    }
    // We read the input parameters
    googlePlaces.freeSearch(freeText, countryCode,successFunction, errorFunction);

});


// This is a function that accesses a GooglePlace service to get the response from it
// Be careful with the parameter
//
// - enhanced
//
// If it's true it means that we have to add the preferred places to the response
//  
Parse.Cloud.define("locationSearch", function(request, response) {
    // Create a promise to mantain with other operations
    var requestPromise = Parse.Promise.as();
    requestPromise.then(function(){
        // We create the Promise to return 
        var successful = new Parse.Promise();
        // we use google search to esecute the query
        googlePlaces.locationSearch(request.params,
            function(httpResponse) {
                // In case of success we have the result of the query
                var jsonResult = JSON.parse(httpResponse.text);
                // Now we have to add the list of preferred places to add to the jsonResult
                var currentUser = Parse.User.current();
                var prefsRelation = currentUser.relation("bestPlaces");
                var relationQuery = prefsRelation.query();
                relationQuery.find().then(function (bestPlaces) {
                    // Here we have the preferred places for the current user
                    console.log("MANAGE BESTPLACES " + bestPlaces.length);
                    var prefsArray = new Array();
                    for (var i = 0; i < bestPlaces.length; i++) {
                        var prefItem = new Object();
                        prefItem.id = bestPlaces[i].get("placeId");
                        prefItem.name = bestPlaces[i].get("name");
                        var types = new Array();
                        types.push(bestPlaces[i].get("types"));
                        prefItem.types = types;
                        prefItem.reference = bestPlaces[i].get("googleReference");
                        prefItem.formatted_address = bestPlaces[i].get("formattedAddress");
                        // Creating Geometry location
                        prefItem.geometry = new Object();
                        prefItem.geometry.location = new Object();
                        prefItem.geometry.location.lat = bestPlaces[i].get("location").latitude;
                        prefItem.geometry.location.lng = bestPlaces[i].get("location").longitude;
                        // We get the current place. We search for the place if present into the 
                        // local DB
                        prefsArray.push(prefItem);
                    }
                    if (prefsArray.length > 0) {
                        jsonResult.preferred = prefsArray;
                    }
                    // Now we have to manage the decoration with roles
                    if (request.params.enhanced == "true") {
                        // We create an associative array that has the placeId as a key
                        // and the place data as value. The preferred place has the precedence
                        // on the Google one
                        var placeItems = new Array();
                        // In this case we have to manage roles for the places
                        // We create an array of all the placeId for both preferred and not places
                        var placeIdArray = new Array();
                        for (var i = 0; i < prefsArray.length; i++) {
                            placeIdArray.push(prefsArray[i].id);
                            placeItems[prefsArray[i].id] = prefsArray[i];
                            console.log("PLACE PREFERITO CON ID " + prefsArray[i].id);
                        }
                        var newResults = new Array();
                        for (var i = 0; i < jsonResult.results.length; i++) {
                            // We check if the place is already present. If not we add to the
                            // new Result. 
                            var fromServerPlaceId = jsonResult.results[i].id;
                            console.log("PLACE DA SERVER CON ID " + fromServerPlaceId);
                            console.log("   KEY " + placeItems[fromServerPlaceId]);
                            if (placeItems[fromServerPlaceId] == null) {
                                console.log("   NON PRESENTE. LO AGGIUNGIAMO");
                                // Place not present so we add it to the results
                                newResults.push(jsonResult.results[i]);
                                // Save reference and id
                                placeIdArray.push(fromServerPlaceId);
                                placeItems[fromServerPlaceId] = jsonResult.results[i];
                            }
                        }
                        if (placeIdArray.length > 0) {
                            jsonResult.results = newResults;
                            // Now we have a set of ids and we found the roles for them
                            var inQuery = new Parse.Query("PeopleRole");
                            inQuery.containedIn("placeId", placeIdArray);
                            inQuery.find().then(function (existingRoles) {
                                // Here we have the list of the roles for the given places
                                for (var i = 0; i < existingRoles.length; i++) {
                                    // We check if is present the property 
                                    var roleItemPlaceId = existingRoles[i].get("placeId");
                                    var placeObj = placeItems[roleItemPlaceId];
                                    if (placeObj.prefsRoles == null) {
                                        placeObj.prefsRoles = new Array();
                                    }
                                    // We add the item to the prefRoles
                                    var roleItem = new Object();                                    roleItem.placeId = roleItemPlaceId;
                                    roleItem.peopleAvatar = existingRoles[i].get("roleAvatar");
                                    roleItem.peopleName = existingRoles[i].get("peopleName");
                                    placeObj.prefsRoles.push(roleItem);
                                }
                                successful.resolve(jsonResult);
                                console.log("optLocationSearch() SUCCESS" + jsonResult);
                                return successful;
                            });
                        } else {
                            // In this case there is nothing to do so we return the result
                            successful.resolve(jsonResult);                         console.log("optLocationSearch() SUCCESS" + jsonResult);
                            return successful;
                        }
                    } else {
                        // We return the jsonResult with preferred if any
                        successful.resolve(jsonResult);                     console.log("optLocationSearch() SUCCESS" + jsonResult);
                        return successful;
                    }
                });
            },
            function(detailError){
                // We manage the error
                successful.reject(jsonResult);                  return successful;
            });
        return Parse.Promise.when(successful).then(function(placeDetailJson){
            response.success(placeDetailJson);
        }, function(detailErrorAgain){
            response.error(detailErrorAgain);
        });
    });
});

// This is a function that accesses a GooglePlace service to get the detail of a place
Parse.Cloud.define("getPlaceDetail", function(request, response) {
    // We read parameters from the request
    var googleReference = request.params.reference;
    var countryCode = request.params.countryCode;
    console.log("getPlaceDetail() called for ref:" + googleReference + " and country:" + countryCode);
    var requestPromise = Parse.Promise.as();
    requestPromise.then(function (){
        var successful = new Parse.Promise();
        console.log("getPlaceDetail() AGAIN googleReference:" + googleReference + " and country:" + countryCode);
        googlePlaces.simplePlaceDetail(countryCode, googleReference,
            function(httpResponse) {
                var jsonResult = JSON.parse(httpResponse.text);
                // We create the information we need for the detail
                var placeDetailData = new Object();
                console.log("getPlaceDetail() SUCCESS" + jsonResult);
                successful.resolve(jsonResult);                 return successful;
            }, function(detailError){
                // We manage the error
                successful.reject(jsonResult);                  return successful;
            });
        return Parse.Promise.when(successful).then(function(placeDetailJson){
            response.success(placeDetailJson);
        }, function(detailErrorAgain){
            response.error(detailErrorAgain);
        });
    });
});


// This service is invoked when the user select a place from GooglePlace. In that case we
// have to check if the place is already into the DB and insert it if not. After that we
// have to add the preferred in the relation with the user.
// The parameters into the options object are these:
//
//  - placeId
//  - reference
//  - languageCode
//  - placeType
// 
Parse.Cloud.define("addAsPreferredForUser", function(request, response) {
    // We check if the place is already into our DB.
    var placeId = request.params.placeId;
    var googleReference = request.params.reference;
    var placeType = request.params.placeType;
    var query = new Parse.Query("Place");
    query.equalTo("placeId", placeId);
    query.find({
        success: function(results) {
            console.log("GETTING DETAIL RESULTS :" + results);
            // If the place is not present we have to insert it into the DB. We invoke
            // the detail for the place to get the main informations
            if (results.length == 0){
                console.log("PLACE WITH ID :" + placeId + " NOT PRESENT!!! ");
                var detailSuccessFunction =  function(httpResponse) {
                    // We parse the text as a JSON object
                    var jsonResult = JSON.parse(httpResponse.text);
                    // Now we have the information about the new place to insert. We execute
                    // an insert
                    var newPlace = new Parse.Object("Place");
                    newPlace.set("name", jsonResult.result.name);
                    newPlace.set("placeId", placeId);
                    newPlace.set("googleReference", googleReference);
                    newPlace.set("types", placeType);
                    newPlace.set("formattedAddress", jsonResult.result.formatted_address);
                    // We create the object of type
                    var latitude = jsonResult.result.geometry.location.lat;
                    var longitude = jsonResult.result.geometry.location.lng;
                    var placeLocation = new Parse.GeoPoint(latitude, longitude);
                    newPlace.set("location", placeLocation);
                    // We have to decorate the JSON with local information (ref or people)
                    // We save the place
                    newPlace.save({
                        success: function(newPlaceAgain) {
                            // We have the new entity so we get the objectId to create the relation for the 
                            // user preferred
                            var currentUser = Parse.User.current();
                            // We create the relation between the user and the place
                            var prefRelation = currentUser.relation("bestPlaces");
                            prefRelation.add(newPlaceAgain);
                            currentUser.save({
                                success: function(saveObject){
                                    console.log("SUCCESS!!!");
                                },
                                error: function(errorObject){
                                    console.log("ERRORRRRR!!!");
                                }
                            });
                            response.success(newPlaceAgain);
                        },
                        error: function (error){
                            response.success("ERROR INSERTING NEWPLACE " + error.message);
                        }
                    });
                }
                // Define callback function for error
                var detailErrorFunction =  function(httpResponse) {
                    response.success(httpResponse.text);
                }
                googlePlaces.placeDetail(request.params, detailSuccessFunction, detailErrorFunction);
            } else {
                // The current place
                var newPlace = results[0];
                // The Place is already present so we just need to manage the relation with the user
                var currentUser = Parse.User.current();
                // We create the relation between the user and the place
                var prefRelation = currentUser.relation("bestPlaces");
                prefRelation.add(newPlace);
                currentUser.save();
                response.success(newPlace);
            }
        },
        error: function(object, error) {
            // error is an instance of Parse.Error.
            response.success("ERROR :" + error.message);
        }
    });
});

// This is a function that register a new people
Parse.Cloud.define("addNewPeople", function(request, response) {
    // We read the parameters 
    var placeId = request.params.placeId;
    var countryCode = request.params.userCountryCode;
    var peopleName = request.params.peopleName;
    var googleReference = request.params.reference;
    var peopleAvatarName = request.params.peopleAvatar;
    var placeType = request.params.placeType;
    var voteValue = request.params.voteValue;
    // This param is useful to force the insert of the role also if doubled
    var forced = request.params.forced;
    // We check for mandatory parameters
    if (placeId == null) {
        response.error("placeId is mandatory!");
        return;
    }
    if (peopleName == null) {
        response.error("peopleName is mandatory!");
        return;
    }
    // We search for the place to add the role to
    console.log("CREATING ROLE FOR PLACE WITH ID: " + placeId);
    console.log("CREATING ROLE FOR PLACE WITH REFERENCE: " + googleReference);
    var query = new Parse.Query("Place");
    query.equalTo("placeId", placeId);
    query.find().then(function(places){
        // Here we should have the list of places for the given id (it should be just 1)
        if (places && places.length == 0) {
            console.log("NO EXISTING PLACE WITH ID: " + placeId);
            // Here we have to insert the Place and go ahead
            return createPromiseForDetail(countryCode, googleReference).then(function (result){
                // Into result we have the details of the place to insert to we have to insert them into DB
                console.log("WE CREATED PLACE ISTANCE FROM GOOGLE: " + result);
                // we have to save it. 
                var successful = new Parse.Promise();
                successful.resolve(result);
                return successful;
            }).then(function (newCreatedPlace) {
                    console.log("WE CREATED PLACE ISTANCE FROM GOOGLE  22: " + newCreatedPlace);
                    return newCreatedPlace.save().then(function (persistedPlace) {
                        console.log("WE PESISTED NEW PLACE: " + persistedPlace);
                        // Create the association with the current user
                        var currentUser = Parse.User.current();                 // We create the relation between the user and the place
                        var prefRelation = currentUser.relation("bestPlaces");
                        prefRelation.add(newCreatedPlace);
                        console.log("CREATING RELATIONW WITH USER " + currentUser);
                        return currentUser.save().then(function (){
                            // We have to return the place
                            var successful = new Parse.Promise();
                            successful.resolve(newCreatedPlace);
                            return successful;
                        });
                    });
                });
        } else {
            // In this case we have to insert the new place and then go on
            console.log("YES!!! PLACE EXISTING WITH ID: " + placeId + " WITH NAME " + places[0].get("name"));
            var successful = new Parse.Promise();
            successful.resolve(places[0]);
            return successful;
        }
    }).then(function (placeForRole){
            // Here we have the place that we have to decorate ###
            var roleQuery = new Parse.Query("PeopleRole");
            roleQuery.equalTo("placeId", placeId);
            return roleQuery.find().then(function (roleResults) {
                // In this case we don't return an error but we return a field that contains the
                // roles already present
                var conflictingRoles = new Array();
                for (var i = 0; i < roleResults.length; i++){
                    if (roleResults[i].get("peopleName").toLowerCase() == peopleName.toLowerCase()) {
                        var itemRole = new Object();
                        itemRole.peopleName = roleResults[i].get("peopleName");
                        itemRole.peopleAvatar = roleResults[i].get("roleAvatar");
                        conflictingRoles.push(itemRole);
                    }
                }
                // Now we have to check if to insert the new role or not using the force param
                if (forced || conflictingRoles.length == 0){
                    // In this case there's no people with the same name so we have
                    // to create the new People entity
                    var newPeople = new Parse.Object("People");
                    newPeople.set("peopleName", peopleName);
                    newPeople.set("peopleAvatar", peopleAvatarName);
                    newPeople.set("confirmed", false);
                    newPeople.set("lowercasePeopleName", peopleName.toLowerCase());
                    return newPeople.save().then(function (newPeopleAgain){
                        // Here we have saved the newPeople because we have to force him
                        // We have the new people we have to create the new role
                        var newRole = new Parse.Object("PeopleRole");
                        newRole.set("creator", Parse.User.current());
                        newRole.set("peopleId", newPeopleAgain.id);
                        newRole.set("people", newPeopleAgain);
                        newRole.set("placeId", placeId);
                        newRole.set("place", placeForRole);
                        newRole.set("placeRoleLocation", placeForRole.get("location"));
                        newRole.set("placeName", placeForRole.get("name"));
                        newRole.set("peopleName", peopleName);
                        newRole.set("lowercasePeopleName", peopleName.toLowerCase());
                        newRole.set("roleAvatar", peopleAvatarName);
                        return newRole.save().then(function (newRoleAgain){
                            // Here we have the new role so we have to create the new Vote
                            var newVote = new Parse.Object("Vote");
                            newVote.set("user", Parse.User.current());
                            newVote.set("people", newPeopleAgain);
                            newVote.set("peopleId", newPeopleAgain.id);
                            newVote.set("place", placeForRole);
                            newVote.set("placeId", placeId);
                            newVote.set("creator", Parse.User.current());
                            newVote.set("value", voteValue);
                            newVote.set("placeRoleLocation", placeForRole.get("location"));                     return newVote.save();
                        });
                    });
                } else {
                    console.log("A SIMILAR ROLE IS PRESENT. WE RETURN ERROR");
                    var conflictPromise = new Parse.Promise();
                    conflictPromise.resolve({code: 201, existingRoles:conflictingRoles});
                    return conflictPromise;
                }
            });
        }).then(function (placeToDecorate) {
            console.log("LAST VALUE" + placeToDecorate);
            return response.success(placeToDecorate);
            /*
             var successful = new Parse.Promise();
             successful.resolve(placeToDecorate);
             return successful;
             */
        }, function(error){
            response.error(error);
        });
});


// This is a function that creates a Promise that returns the information related
// to the detail of a Google Place. This returns a Promise for the Place with the
// details got from Google place
function createPromiseForDetail(countryCode, googleRererence) {
    var requestPromise = Parse.Promise.as();
    return requestPromise.then(function (){
        var successful = new Parse.Promise();
        googlePlaces.simplePlaceDetail(countryCode, googleRererence,
            function(httpResponse){
                var jsonResult = JSON.parse(httpResponse.text);
                var newPlace = new Parse.Object("Place");
                newPlace.set("name", jsonResult.result.name);               newPlace.set("placeId", jsonResult.result.id);
                newPlace.set("googleReference", jsonResult.result.reference);
                var typesArray = jsonResult.result.types;
                if (typesArray.length > 0) {
                    newPlace.set("types", typesArray[0]);
                }
                newPlace.set("formattedAddress", jsonResult.result.formatted_address);              // We create the object of type
                var latitude = jsonResult.result.geometry.location.lat;
                var longitude = jsonResult.result.geometry.location.lng;                var placeLocation = new Parse.GeoPoint(latitude, longitude);
                newPlace.set("location", placeLocation);
                successful.resolve(newPlace);                   return successful;
            }, function(detailError){
                // We manage the error
                successful.reject(jsonResult);                  return successful;
            });
        return Parse.Promise.when(successful);
    });
}


// This is a function that search for the people around the user lcoation
Parse.Cloud.define("findPeopleAround", function(request, response) {
    // We read the parameters 
    var countryCode = request.params.countryCode;
    var latitude = request.params.latitude;
    var longitude = request.params.longitude;
    var radius = request.params.radius;
    // Calculate support data
    console.log("LATITUDE" + latitude);
    console.log("LONGITUDE" + longitude);
    console.log("RARIUS" + radius);
    var refPoint = new Parse.GeoPoint(latitude, longitude);
    var distInKm = radius / 1000;
    var query = new Parse.Query("PeopleRole");
    query.withinKilometers("placeRoleLocation", refPoint, distInKm);
    query.find().then(function (nearRoles){
        response.success(nearRoles);
    }, function (findError) {
        response.error(findError.message);
    });
});

// This is a function that search people by name
Parse.Cloud.define("searchPeople", function(request, response) {
    // We read the parameters 
    var countryCode = request.params.countryCode;
    var peopleName = request.params.peopleName;
    // Calculate support data
    console.log("searchPeople() PEOPLE NAME" + peopleName);
    console.log("searchPeople () LONGITUDE" + countryCode);
    var query = new Parse.Query("PeopleRole");
    query.contains("lowercasePeopleName", peopleName);
    query.find().then(function (matchingPeople){
        response.success(matchingPeople);
    }, function (findError) {
        response.error(findError.message);
    });
});


// This is the function that permits to vote an existing user.
Parse.Cloud.define("voteExistingPeople", function(request, response) {
    // We read the parameters 
    var countryCode = request.params.countryCode;
    var roleId = request.params.roleId;
    var peopleAvatar = request.params.peopleAvatarName;
    var voteValue = request.params.voteValue;
    console.log("voteExistingPeople() COUNTRY CODE" + countryCode);
    console.log("voteExistingPeople() ROLE ID" + roleId);
    console.log("voteExistingPeople() PEOPLE AVATAR" + peopleAvatar);
    console.log("voteExistingPeople() VOTE VALUE" + voteValue);
    // We get the role for the given id
    var query = new Parse.Query("PeopleRole");
    query.get(roleId).then(function(roleToVote){
        if (roleToVote == null) {
            console.log("voteExistingPeople() NO ROLE WITH ID" + roleId);
            response.error("No role with the given id ");
            return;
        }
        var placeForRole = roleToVote.get("place");
        // If present we create the Vote for the user
        var newVote = new Parse.Object("Vote");
        newVote.set("user", Parse.User.current());
        newVote.set("people", roleToVote.get("people"));
        newVote.set("peopleId", roleToVote.get("peopleId"));
        newVote.set("place", roleToVote.get("place"));
        newVote.set("placeId", roleToVote.get("placeId"));
        newVote.set("creator", Parse.User.current());
        newVote.set("value", voteValue);
        newVote.set("placeRoleLocation", placeForRole.get("location"));
        return newVote.save().then(function(newVote){
            response.success(newVote);
        });
    }, function(getError) {
        response.error(getError.message);
    });
});